package com.lcq.demo.service;

import com.lcq.demo.common.entity.DynamoDbPage;
import com.lcq.demo.common.entity.PageResult;
import com.lcq.demo.common.enums.BucketNameEnum;
import com.lcq.demo.entity.dto.MusicDTO;
import com.lcq.demo.entity.dynamodb.Music;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.paginators.QueryIterable;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.lcq.demo.common.enums.BucketNameEnum.TEST_BUCKET_NAME;

/**
 * @author 240701005
 */
@Service
@Slf4j
public class DynamoDbService {

    @Resource
    private DynamoDbClient dynamoDbClient;

    private final static String ARTIST_EXPRESS = "Artist = :Artist";

    private final static String TABLE_NAME = "test_music";

    /**
     * 查询单个Music
     */
    public Music getItem(MusicDTO musicDTO) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("SongTitle", AttributeValue.builder().s(musicDTO.getSongTitle()).build());
        key.put("Artist", AttributeValue.builder().s(musicDTO.getArtist()).build());
        GetItemRequest request = GetItemRequest.builder()
                .tableName(musicDTO.getTableName())
                .key(key)
                .build();
        try {
            GetItemResponse response = dynamoDbClient.getItem(request);
            if (!response.hasItem()) {
                return null;
            }
            Map<String, AttributeValue> item = response.item();
            return buildMusicFromItem(item);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 查询项目列表(未分页)
     */
    public List<Music> getItemList(String tableName, String artist) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put(":Artist", AttributeValue.builder().s(artist).build());
        QueryRequest queryRequest = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression(ARTIST_EXPRESS)
                .expressionAttributeValues(key)
                .build();
        QueryResponse queryResponse = dynamoDbClient.query(queryRequest);
        List<Music> musicList = new ArrayList<>();
        queryResponse.items().forEach(item -> {
            Music music = buildMusicFromItem(item);
            musicList.add(music);
        });
        return musicList;
    }

    /**
     * 根据歌手获取MusicList
     */
    public DynamoDbPage<Music> getMusicListByArtist(MusicDTO musicDTO) {
        DynamoDbPage<Music> pageResult = new DynamoDbPage<>();
        List<Music> musicList = new ArrayList<>();
        Map<String, AttributeValue> key = new HashMap<>();
        key.put(":Artist", AttributeValue.builder().s(musicDTO.getArtist()).build());
        Map<String, AttributeValue> exclusiveStartKey = null;
        do {
            musicList.clear();
            QueryRequest request = QueryRequest.builder()
                    .tableName(musicDTO.getTableName())
                    .keyConditionExpression(ARTIST_EXPRESS)
                    .expressionAttributeValues(key)
                    .limit(musicDTO.getPageSize())
                    .exclusiveStartKey(exclusiveStartKey)
                    .build();
            QueryResponse response = dynamoDbClient.query(request);
            response.items().forEach(item -> {
                Music music = buildMusicFromItem(item);
                musicList.add(music);
            });
            exclusiveStartKey = response.lastEvaluatedKey();
        } while ((exclusiveStartKey != null && !exclusiveStartKey.isEmpty()));
        pageResult.setRecord(musicList);
        pageResult.setPageSize(musicDTO.getPageSize());
        return pageResult;
    }

    /**
     * 批量删除
     */
    public void batchDeleteItems(List<MusicDTO> musicList) {
        List<WriteRequest> writeRequests = new ArrayList<>();
        for (MusicDTO music : musicList) {
            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(music.getArtist()).build());
            key.put("SongTitle", AttributeValue.builder().s(music.getSongTitle()).build());
            writeRequests.add(WriteRequest.builder()
                    .deleteRequest(DeleteRequest.builder().key(key).build()).build());
        }
        execBatchRequest(writeRequests, dynamoDbClient);
    }

    /**
     * 批量新增
     */
    public void batchInsertItems(List<MusicDTO> musicList) {
        List<WriteRequest> writeRequests = new ArrayList<>();
        for (MusicDTO music : musicList) {
            Map<String, AttributeValue> key = new HashMap<>();
            key.put("Artist", AttributeValue.builder().s(music.getArtist()).build());
            key.put("SongTitle", AttributeValue.builder().s(music.getSongTitle()).build());
            if (!StringUtils.isEmpty(music.getAlbumTitle())) {
                key.put("AlbumTitle", AttributeValue.builder().s(music.getAlbumTitle()).build());
            }
            if (music.getAwards() != null) {
                key.put("Awards", AttributeValue.builder().n(String.valueOf(music.getAwards())).build());
            }
            if (music.getLike() != null) {
                key.put("like", AttributeValue.builder().n(String.valueOf(music.getLike())).build());
            }
            writeRequests.add(WriteRequest.builder().putRequest(PutRequest.builder().item(key).build()).build());
        }
        execBatchRequest(writeRequests, dynamoDbClient);
    }

    private static void execBatchRequest(List<WriteRequest> writeRequests, DynamoDbClient dynamoDbClient) {
        Map<String, List<WriteRequest>> batchWriteItemRequestMap = new HashMap<>();
        batchWriteItemRequestMap.put(TABLE_NAME, writeRequests);
        BatchWriteItemRequest batchWriteItemRequest = BatchWriteItemRequest.builder()
                .requestItems(batchWriteItemRequestMap)
                .build();
        BatchWriteItemResponse batchWriteItemResponse = dynamoDbClient.batchWriteItem(batchWriteItemRequest);
        log.info("DynamoDbService.batchDeleteItems successfully :{}", batchWriteItemResponse);
    }

    /**
     * 插入文档
     */
    public boolean insertItem(Music music) {
        String tableName = "test_music";
        Map<String, AttributeValue> item = new HashMap<>();
        item.put("Artist", AttributeValue.builder().s(music.getArtist()).build());
        item.put("SongTitle", AttributeValue.builder().s(music.getSongTitle()).build());
        item.put("AlbumTitle", AttributeValue.builder().s(music.getAlbumTitle()).build());
        item.put("Awards", AttributeValue.builder().n(music.getAwards().toString()).build());
        item.put("like", AttributeValue.builder().n(music.getLike().toString()).build());
        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(item)
                .build();
        try {
            PutItemResponse response = dynamoDbClient.putItem(request);
            return true;
        } catch (ResourceNotFoundException e) {
            return false;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            return false;
        }
    }

    /**
     * 修改数据
     */
    public boolean update(MusicDTO musicDTO) {
        Map<String, AttributeValue> itemKey = new HashMap<>();
        itemKey.put("SongTitle", AttributeValue.builder().s(musicDTO.getSongTitle()).build());
        itemKey.put("Artist", AttributeValue.builder().s(musicDTO.getArtist()).build());
        Map<String, AttributeValue> expressionAttributeValues  = new HashMap<>();
        Map<String, String> expressionAttributeNames = new HashMap<>();
        expressionAttributeNames.put("#likes", "like");
        expressionAttributeValues.put(":newLike", AttributeValue.builder().n(musicDTO.getLike().toString()).build());
        expressionAttributeValues.put(":newAwards", AttributeValue.builder().n(musicDTO.getAwards().toString()).build());
        expressionAttributeValues.put(":newAlbumTitle", AttributeValue.builder().s(musicDTO.getAlbumTitle()).build());
        String updateExpression = "SET #likes = :newLike, Awards = :newAwards, AlbumTitle = :newAlbumTitle";
        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName(musicDTO.getTableName())
                .key(itemKey)
                .updateExpression(updateExpression)
                .expressionAttributeNames(expressionAttributeNames)
                .expressionAttributeValues(expressionAttributeValues)
                .build();
        try {
            UpdateItemResponse updateItemResponse = dynamoDbClient.updateItem(request);
            return updateItemResponse.sdkHttpResponse().statusCode() == 200;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 删除数据
     */
    public Boolean delete(Music music, String tableName) {
        Map<String, AttributeValue> key = new HashMap<>();
        key.put("SongTitle", AttributeValue.builder().s(music.getSongTitle()).build());
        key.put("Artist", AttributeValue.builder().s(music.getArtist()).build());
        DeleteItemRequest request = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(key)
                .build();
            DeleteItemResponse deleteItemResponse = dynamoDbClient.deleteItem(request);
        return deleteItemResponse.sdkHttpResponse().statusCode() == 200;
    }

    /**
     * 根据二级索引查询
     */
    public DynamoDbPage<Music> getByIndex(MusicDTO musicDto) {
        DynamoDbPage<Music> pageResult = new DynamoDbPage<>();
        String tableName = musicDto.getTableName();
        String indexName = musicDto.getIndexName();
        Map<String, AttributeValue> map = new HashMap<>();
        List<AttributeValue> awardsRange = new ArrayList<>();
        awardsRange.add(AttributeValue.builder().n(musicDto.getMinAwards().toString()).build());
        awardsRange.add(AttributeValue.builder().n(musicDto.getMaxAwards().toString()).build());
        map.put(":Artist", AttributeValue.builder().s(musicDto.getArtist()).build());
        map.put(":minAwards", AttributeValue.builder().n(musicDto.getMinAwards().toString()).build());
        map.put(":maxAwards", AttributeValue.builder().n(musicDto.getMaxAwards().toString()).build());
        QueryRequest queryRequest = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("Artist = :Artist and Awards between :minAwards and :maxAwards")
                .expressionAttributeValues(map)
                .scanIndexForward(false)
                .build();
        pageResult.setRecord(dynamoDbClient.query(queryRequest).items().stream()
                .map(this::buildMusicFromItem)
                .collect(Collectors.toList()));
        pageResult.setPageSize(musicDto.getPageSize());
        return pageResult;
    }

    /**
     * AttributeValue转换Music
     */
    private Music buildMusicFromItem(Map<String, AttributeValue> item) {
        Music music = new Music();
        for (Map.Entry<String, AttributeValue> entry : item.entrySet()) {
            String key = entry.getKey();
            AttributeValue value = entry.getValue();
            switch (key) {
                case "Artist":
                    music.setArtist(value.s());
                    break;
                case "SongTitle":
                    music.setSongTitle(value.s());
                    break;
                case "AlbumTitle":
                    music.setAlbumTitle(value.s());
                    break;
                case "Awards":
                    music.setAwards(Integer.valueOf(value.n()));
                    break;
                case "like":
                    music.setLike(Integer.valueOf(value.n()));
                    break;
                default:
                    break;
            }
        }
        return music;
    }


}
